[分享]浅谈电商库存模型

本文讨论内容不包含秒杀,仅仅是最普遍的库存模型而已,重点讨论如何避免超卖少卖


1、 基本概念

1.1 超卖

实际库存已经为0,但是依旧卖出去了。可能导致商家无货可发。

1.2 少卖

明明有货,但是库存被锁定,导致商家货卖不出去。


2、 分布式事务简述

2.1 消息事务+最终一致性

所谓的消息事务就是基于消息中间件的两阶段提交,本质上是对消息中间件的一种特殊利用,
它是将本地事务和发消息放在了一个分布式事务里,保证要么本地操作成功成功并且对外发消息成功,
要么两者都失败,开源的RocketMQ就支持这一特性,具体原理如下:

图 fenzu

  • 1、A系统向消息中间件发送一条预备消息
  • 2、消息中间件保存预备消息并返回成功
  • 3、A执行本地事务
  • 4、A发送提交消息给消息中间件

通过以上4步完成了一个消息事务。对于以上的4个步骤,每个步骤都可能产生错误,下面一一分析:

  • 步骤一出错,则整个事务失败,不会执行A的本地操作
  • 步骤二出错,则整个事务失败,不会执行A的本地操作
  • 步骤三出错,这时候需要回滚预备消息,怎么回滚?答案是A系统实现一个消息中间件的回调接口,消息中间件会去不断执行回调接口,检查A事务执行是否执行成功,如果失败则回滚预备消息
  • 步骤四出错,这时候A的本地事务是成功的,那么消息中间件要回滚A吗?答案是不需要,其实通过回调接口,消息中间件能够检查到A执行成功了,这时候其实不需要A发提交消息了,消息中间件可以自己对消息进行提交,从而完成整个消息事务

2.2 TCC编程模式

所谓的TCC编程模式,也是两阶段提交的一个变种。TCC提供了一个编程框架,将整个业务逻辑分为三块:Try、Confirm和Cancel三个操作。
以在线下单为例,Try阶段会去扣库存,Confirm阶段则是去更新订单状态,如果更新订单失败,则进入Cancel阶段,会去恢复库存。
总之,TCC就是通过代码人为实现了两阶段提交,不同的业务场景所写的代码都不一样,复杂度也不一样,因此,这种模式并不能很好地被复用。


3、 库存模型

  • 1.下单的时候每个订单都会有预占库存。
  • 2.订单出库或取消解锁预占。
  • 3.可售库存 = 现货库存 - 预占库存

具体下单操作流程是

下单 –> 预占(预占库存+1) –> 支付 –> 出库(释放预占并减库存,涉及到分布式事务) –> 完成

重点讨论此方案的意义,跟出库部分。

3.1 为什么设计成预占库存?

以下均为个人理解

  • 1.库存和下单分离,下单了不会实际减少库存。真正的实物库存跟逻辑库存分离,方便(盘库、补货等)仅仅用实物库存的场景跟高并发的场景(下单)分离。
  • 2.预占可以减少库存表的操作频率,减库存可以在发货的时候统一处理。(操作多次预占表后仅在必要时操作一次库存表,跟1有共通之处)

3.2 预占库存怎么加?

  • 1.可以用mysql乐观锁(version递增避免aba)
  • 2.可以用redis watch(同样基于cas,无aba)

3.3 出库部分怎么保证事务?

  • 1.先出库还是先释放预占?

    为了避免超卖选择先减库存,再释放预占。

  • 2.如何保证事务一致性?

    采用消息机制的2pc保证分布式事务。